package cn.lg.soar.core.util.easyexcel.merge;

import cn.lg.soar.core.util.easyexcel.PoiExcelUtil;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * excel导出单元格合并处理器
 * @author luguoxiang
 * @date 2022-05-07
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public class MergeWriteHandler implements CellWriteHandler {

    private final List<CellRangeAddress> cellRangeAddressList;
    private final Map<Integer, MergeTemp> rowMergeMap;
    private final Map<Integer, MergeTemp> colMergeMap;
    private boolean uninitialized = true;

    public MergeWriteHandler(MergeStrategy strategy) {
        this.cellRangeAddressList = strategy.getCellRangeAddressList();
        Map<Integer, int[]> rowMap = strategy.getRowMergeMap();
        Map<Integer, MergeTemp> rowMergeMap = new HashMap<>(rowMap.size());
        rowMap.forEach((k, v) -> {
            if (v.length == 1) {
                rowMergeMap.put(k, new MergeTemp(v[0], 0));
            } else {
                rowMergeMap.put(k, new MergeTemp(v[0], v[1]));
            }
        });
        this.rowMergeMap = rowMergeMap;

        Map<Integer, int[]> colMap = strategy.getColMergeMap();
        Map<Integer, MergeTemp> colMergeMap = new HashMap<>(colMap.size());
        colMap.forEach((k, v) -> {
            if (v.length == 1) {
                colMergeMap.put(k, new MergeTemp(v[0], 0));
            } else {
                colMergeMap.put(k, new MergeTemp(v[0], v[1]));
            }
        });
        this.colMergeMap = colMergeMap;
    }

    @Override
    public void afterCellDataConverted(CellWriteHandlerContext context) {
        CellWriteHandler.super.afterCellDataConverted(context);
    }

    @Override
    public void afterCellDispose(CellWriteHandlerContext context) {
        if (context.getHead()) {
            return;
        }
        Cell cell = context.getCell();
        Sheet sheet = cell.getSheet();
        if (uninitialized) {
            cellRangeAddressList.forEach(sheet::addMergedRegionUnsafe);
            uninitialized = false;
        }
        // 自动行合并
        autoRowMerge(cell, sheet);

        // 自动列合并
        autoColMerge(cell, sheet);
    }

    /**
     * 自动行合并（纵向合并）
     * @param cell 单元格
     * @return
     */
    private boolean autoRowMerge(Cell cell, Sheet sheet) {
        int colIndex = cell.getColumnIndex();
        // 自动行合并
        MergeTemp mergeTemp = rowMergeMap.get(colIndex);
        if (mergeTemp == null) {
            return false;
        }
        int rowIndex = cell.getRowIndex();
        if (mergeTemp.startIndex > rowIndex) {
            return false;
        }
        if (mergeTemp.endIndex != -1 && mergeTemp.endIndex < rowIndex) {
            return false;
        }
        Object curData = PoiExcelUtil.getCellValue(cell);
        if (curData != null && Objects.equals(mergeTemp.data, curData)) {
            CellRangeAddress address;
            if (mergeTemp.mergeIndex == null) {
                address = new CellRangeAddress(
                        rowIndex - 1,
                        rowIndex,
                        colIndex,
                        colIndex
                );
                sheet.addMergedRegionUnsafe(address);
                mergeTemp.mergeIndex = sheet.getMergedRegions().size() - 1;
            } else {
                address = sheet.getMergedRegion(mergeTemp.mergeIndex);
                address.setLastRow(address.getLastRow() + 1);
            }
            return true;
        }
        mergeTemp.mergeIndex = null;
        mergeTemp.data = curData;
        return true;
    }

    /**
     * 自动列合并（横向合并）
     * @param cell 单元格
     * @return
     */
    private boolean autoColMerge(Cell cell, Sheet sheet) {
        int rowIndex = cell.getRowIndex();
        // 自动行合并
        MergeTemp mergeTemp = colMergeMap.get(rowIndex);
        if (mergeTemp == null) {
            return false;
        }
        int colIndex = cell.getColumnIndex();
        if (mergeTemp.startIndex > colIndex) {
            return false;
        }
        if (mergeTemp.endIndex != -1 && mergeTemp.endIndex < colIndex) {
            return false;
        }
        Object curData = PoiExcelUtil.getCellValue(cell);
        if (curData != null && Objects.equals(mergeTemp.data, curData)) {
            CellRangeAddress address;
            if (mergeTemp.mergeIndex == null) {
                address = new CellRangeAddress(
                        rowIndex,
                        rowIndex,
                        colIndex - 1,
                        colIndex
                );
                sheet.addMergedRegionUnsafe(address);
                mergeTemp.mergeIndex = sheet.getMergedRegions().size() - 1;
            } else {
                address = sheet.getMergedRegion(mergeTemp.mergeIndex);
                address.setLastColumn(address.getLastColumn() + 1);
            }
            return true;
        }
        mergeTemp.mergeIndex = null;
        mergeTemp.data = curData;
        return true;
    }

}
